Send the focus on to the parent when there was no focus widget before and
authorOwen Taylor <otaylor@redhat.com>
Mon, 25 Aug 2003 21:46:57 +0000 (21:46 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Mon, 25 Aug 2003 21:46:57 +0000 (21:46 +0000)
Wed Aug 20 21:26:49 2003  Owen Taylor  <otaylor@redhat.com>

        * gtk/gtkplug.c (gtk_plug_focus): Send the focus on
        to the parent when there was no focus widget before
        and no focus widget after - that is, when there are
        no focusable widgets. (#108678, help tracking it
        down from Padraig O'Briain, Federico Mena Quintero, )

        * gtk/gtkxembed.[ch]: Move various shared utilities
        for the XEMBED protocol eused by GtkPlug and GtkSocket
        here.

        * gtk/gtkxembed.[ch] gtkplug.c gtksocket.c: Implement
        a flag bit that is sent with focus mesages to indicate
        that the focus has wrapped around on the toplevel;
        use this bit to catch infinite loops when there is no
        focusable widget at ll in the entire toplevel.

        * tests/testsocket.c (child_read_watch): Remove an
        extraneous unref.

        * gtk/gtkplug.c gtk/gtksocket.c gtk/gtkxembed.h:
        Up XEMBED protocol version to 1, add logic for
        sending the right version in XEMBED_EMBEDDED_NOTIFY.

        * gtk/gtksocket.c (gtk_socket_add_window): Send
        the embedder window in the XEMBED_EMBEDDED_NOTIFY
        as the spec requires.

docs/reference/gtk/Makefile.am
gtk/Makefile.am
gtk/gtkplug.c
gtk/gtksocket.c
gtk/gtkxembed.c [new file with mode: 0644]
gtk/gtkxembed.h [new file with mode: 0644]
gtk/xembed.h

index eb4431356fb000951990af79a8250bd0b133b2c0..52d9bceabebad09c77ebaaf989530b9a4ee049b1 100644 (file)
@@ -46,6 +46,7 @@ IGNORE_HFILES=                        \
        gtktexttypes.h          \
        gtktextutil.h           \
        gtktypebuiltins.h       \
+       gtkxembed.h             \
        xembed.h
 
 # CFLAGS and LDFLAGS for compiling scan program. Only needed
index 5d1c6216516c34c446892261b1c7d50718b1068c..9ca5c77236a9a5812250cc25905020450abd80d5 100644 (file)
@@ -445,7 +445,9 @@ gtk_c_sources =                 \
 
 gtk_plug_c_sources =            \
        gtkplug.c               \
-       gtksocket.c
+       gtksocket.c             \
+       gtkxembed.c             \
+       gtkxembed.h
 
 # we use our own built_sources variable rules to avoid automake's
 # BUILT_SOURCES oddities
index 17345563ed537a2dd6d8a99615f67d88086972ac..d0c3496a642e43bfd38c1997963d94d779d2f9b0 100644 (file)
@@ -33,7 +33,7 @@
 #include "gdk/gdkkeysyms.h"
 #include "x11/gdkx.h"
 
-#include "xembed.h"
+#include "gtkxembed.h"
 
 static void            gtk_plug_class_init            (GtkPlugClass     *klass);
 static void            gtk_plug_init                  (GtkPlug          *plug);
@@ -61,12 +61,6 @@ static GdkFilterReturn gtk_plug_filter_func           (GdkXEvent        *gdk_xev
                                                       gpointer          data);
 
 static void handle_modality_off        (GtkPlug       *plug);
-static void send_xembed_message        (GtkPlug       *plug,
-                                       glong          message,
-                                       glong          detail,
-                                       glong          data1,
-                                       glong          data2,
-                                       guint32        time);
 static void xembed_set_info            (GdkWindow     *window,
                                        unsigned long  flags);
 
@@ -698,8 +692,8 @@ gtk_plug_set_focus (GtkWindow *window,
 
   if (focus && !window->has_toplevel_focus)
     {
-      send_xembed_message (plug, XEMBED_REQUEST_FOCUS, 0, 0, 0,
-                          gtk_get_current_event_time ());
+      _gtk_xembed_send_message (plug->socket_window,
+                               XEMBED_REQUEST_FOCUS, 0, 0, 0);
     }
 }
 
@@ -741,9 +735,8 @@ add_grabbed_key (gpointer key, gpointer val, gpointer data)
   if (!plug->grabbed_keys ||
       !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
     {
-      send_xembed_message (plug, XEMBED_GTK_GRAB_KEY, 0, 
-                          grabbed_key->accelerator_key, grabbed_key->accelerator_mods,
-                          gtk_get_current_event_time ());
+      _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_GRAB_KEY, 0, 
+                               grabbed_key->accelerator_key, grabbed_key->accelerator_mods);
     }
 }
 
@@ -753,9 +746,8 @@ add_grabbed_key_always (gpointer key, gpointer val, gpointer data)
   GrabbedKey *grabbed_key = key;
   GtkPlug *plug = data;
 
-  send_xembed_message (plug, XEMBED_GTK_GRAB_KEY, 0, 
-                      grabbed_key->accelerator_key, grabbed_key->accelerator_mods,
-                      gtk_get_current_event_time ());
+  _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_GRAB_KEY, 0, 
+                           grabbed_key->accelerator_key, grabbed_key->accelerator_mods);
 }
 
 static void
@@ -767,9 +759,8 @@ remove_grabbed_key (gpointer key, gpointer val, gpointer data)
   if (!plug->grabbed_keys ||
       !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
     {
-      send_xembed_message (plug, XEMBED_GTK_UNGRAB_KEY, 0, 
-                          grabbed_key->accelerator_key, grabbed_key->accelerator_mods,
-                          gtk_get_current_event_time ());
+      _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_UNGRAB_KEY, 0, 
+                               grabbed_key->accelerator_key, grabbed_key->accelerator_mods);
     }
 }
 
@@ -812,6 +803,29 @@ gtk_plug_keys_changed (GtkWindow *window)
     }
 }
 
+static void
+focus_to_parent (GtkPlug          *plug,
+                GtkDirectionType  direction)
+{
+  XEmbedMessageType message = XEMBED_FOCUS_PREV; /* Quiet GCC */
+  
+  switch (direction)
+    {
+    case GTK_DIR_UP:
+    case GTK_DIR_LEFT:
+    case GTK_DIR_TAB_BACKWARD:
+      message = XEMBED_FOCUS_PREV;
+      break;
+    case GTK_DIR_DOWN:
+    case GTK_DIR_RIGHT:
+    case GTK_DIR_TAB_FORWARD:
+      message = XEMBED_FOCUS_NEXT;
+      break;
+    }
+  
+  _gtk_xembed_send_focus_message (plug->socket_window, message, 0);
+}
+
 static gboolean
 gtk_plug_focus (GtkWidget        *widget,
                GtkDirectionType  direction)
@@ -841,40 +855,18 @@ gtk_plug_focus (GtkWidget        *widget,
            }
          
          gtk_window_set_focus (GTK_WINDOW (container), NULL);
-
-         if (!GTK_CONTAINER (window)->focus_child)
-           {
-             gint message = -1;
-
-             switch (direction)
-               {
-               case GTK_DIR_UP:
-               case GTK_DIR_LEFT:
-               case GTK_DIR_TAB_BACKWARD:
-                 message = XEMBED_FOCUS_PREV;
-                 break;
-               case GTK_DIR_DOWN:
-               case GTK_DIR_RIGHT:
-               case GTK_DIR_TAB_FORWARD:
-                 message = XEMBED_FOCUS_NEXT;
-                 break;
-               }
-             
-             send_xembed_message (plug, message, 0, 0, 0,
-                                  gtk_get_current_event_time ());
-           }
        }
-
-      return FALSE;
     }
   else
     {
       /* Try to focus the first widget in the window */
-      
       if (bin->child && gtk_widget_child_focus (bin->child, direction))
         return TRUE;
     }
 
+  if (!GTK_CONTAINER (window)->focus_child)
+    focus_to_parent (plug, direction);
+
   return FALSE;
 }
 
@@ -887,48 +879,13 @@ gtk_plug_check_resize (GtkContainer *container)
     GTK_CONTAINER_CLASS (bin_class)->check_resize (container);
 }
 
-static void
-send_xembed_message (GtkPlug *plug,
-                    glong      message,
-                    glong      detail,
-                    glong      data1,
-                    glong      data2,
-                    guint32    time)
-{
-  if (plug->socket_window)
-    {
-      GdkDisplay *display = gdk_drawable_get_display (plug->socket_window);
-      XEvent xevent;
-
-      GTK_NOTE(PLUGSOCKET,
-              g_message ("GtkPlug: Sending XEMBED message of type %ld", message));
-
-      xevent.xclient.window = GDK_WINDOW_XWINDOW (plug->socket_window);
-      xevent.xclient.type = ClientMessage;
-      xevent.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED");
-      xevent.xclient.format = 32;
-      xevent.xclient.data.l[0] = time;
-      xevent.xclient.data.l[1] = message;
-      xevent.xclient.data.l[2] = detail;
-      xevent.xclient.data.l[3] = data1;
-      xevent.xclient.data.l[4] = data2;
-
-      gdk_error_trap_push ();
-      XSendEvent (GDK_WINDOW_XDISPLAY(plug->socket_window),
-                 GDK_WINDOW_XWINDOW (plug->socket_window),
-                 False, NoEventMask, &xevent);
-      gdk_display_sync (display);
-      gdk_error_trap_pop ();
-    }
-}
-
 static void
 focus_first_last (GtkPlug          *plug,
                  GtkDirectionType  direction)
 {
   GtkWindow *window = GTK_WINDOW (plug);
   GtkWidget *parent;
-  
+
   if (window->focus_widget)
     {
       parent = window->focus_widget->parent;
@@ -977,7 +934,7 @@ xembed_set_info (GdkWindow     *window,
 
   Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
 
-  buffer[1] = 0;               /* Protocol version */
+  buffer[1] = GTK_XEMBED_PROTOCOL_VERSION;
   buffer[1] = flags;
 
   XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
@@ -988,17 +945,17 @@ xembed_set_info (GdkWindow     *window,
 }
 
 static void
-handle_xembed_message (GtkPlug   *plug,
-                      glong      message,
-                      glong      detail,
-                      glong      data1,
-                      glong      data2,
-                      guint32    time)
+handle_xembed_message (GtkPlug           *plug,
+                      XEmbedMessageType  message,
+                      glong              detail,
+                      glong              data1,
+                      glong              data2,
+                      guint32            time)
 {
   GtkWindow *window = GTK_WINDOW (plug);
-  
+
   GTK_NOTE (PLUGSOCKET,
-           g_message ("GtkPlug: Message of type %ld received", message));
+           g_message ("GtkPlug: Message of type %d received", message));
   
   switch (message)
     {
@@ -1044,12 +1001,12 @@ handle_xembed_message (GtkPlug   *plug,
     case XEMBED_REQUEST_FOCUS:
     case XEMBED_FOCUS_NEXT:
     case XEMBED_FOCUS_PREV:
-      g_warning ("GtkPlug: Invalid _XEMBED message of type %ld received", message);
+      g_warning ("GtkPlug: Invalid _XEMBED message of type %d received", message);
       break;
       
     default:
       GTK_NOTE(PLUGSOCKET,
-              g_message ("GtkPlug: Ignoring unknown _XEMBED message of type %ld", message));
+              g_message ("GtkPlug: Ignoring unknown _XEMBED message of type %d", message));
       break;
     }
 }
@@ -1071,14 +1028,15 @@ gtk_plug_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
     case ClientMessage:
       if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
        {
+         _gtk_xembed_push_message (xevent);
          handle_xembed_message (plug,
                                 xevent->xclient.data.l[1],
                                 xevent->xclient.data.l[2],
                                 xevent->xclient.data.l[3],
                                 xevent->xclient.data.l[4],
                                 xevent->xclient.data.l[0]);
+         _gtk_xembed_pop_message ();
                                 
-
          return GDK_FILTER_REMOVE;
        }
       else if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW"))
index 7469a4745f55a9975b7f4bba7a4c00385731e1fc..feaa594f16cfaa7d5f213b8aea47aa4f465493de 100644 (file)
@@ -38,7 +38,7 @@
 
 #include "x11/gdkx.h"
 
-#include "xembed.h"
+#include "gtkxembed.h"
 
 typedef struct _GtkSocketPrivate GtkSocketPrivate;
 
@@ -86,12 +86,6 @@ static GdkFilterReturn gtk_socket_filter_func (GdkXEvent       *gdk_xevent,
                                               GdkEvent        *event,
                                               gpointer         data);
 
-static void     send_xembed_message (GtkSocket     *socket,
-                                    glong          message,
-                                    glong          detail,
-                                    glong          data1,
-                                    glong          data2,
-                                    guint32        time);
 static gboolean xembed_get_info     (GdkWindow     *gdk_window,
                                     unsigned long *version,
                                     unsigned long *flags);
@@ -690,16 +684,11 @@ socket_update_focus_in (GtkSocket *socket)
       socket->focus_in = focus_in;
 
       if (focus_in)
-       {
-         send_xembed_message (socket, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0,
-                              gtk_get_current_event_time ());
-       }
+       _gtk_xembed_send_focus_message (socket->plug_window,
+                                       XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT);
       else
-       {
-         send_xembed_message (socket, XEMBED_FOCUS_OUT, 0, 0, 0,
-                              gtk_get_current_event_time ());
-      
-       }
+       _gtk_xembed_send_message (socket->plug_window,
+                                 XEMBED_FOCUS_OUT, 0, 0, 0);
     }
 }
 
@@ -720,10 +709,9 @@ socket_update_active (GtkSocket *socket)
     {
       socket->active = active;
 
-      send_xembed_message (socket,
-                          active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE,
-                          0, 0, 0,
-                          gtk_get_current_event_time ());
+      _gtk_xembed_send_message (socket->plug_window,
+                               active ? XEMBED_WINDOW_ACTIVATE : XEMBED_WINDOW_DEACTIVATE,
+                               0, 0, 0);
     }
 }
 
@@ -773,9 +761,9 @@ gtk_socket_grab_notify (GtkWidget *widget,
   GtkSocket *socket = GTK_SOCKET (widget);
 
   if (!socket->same_app)
-    send_xembed_message (GTK_SOCKET (widget),
-                        was_grabbed ? XEMBED_MODALITY_OFF : XEMBED_MODALITY_ON,
-                        0, 0, 0, gtk_get_current_event_time ());
+    _gtk_xembed_send_message (GTK_SOCKET (widget)->plug_window,
+                             was_grabbed ? XEMBED_MODALITY_OFF : XEMBED_MODALITY_ON,
+                             0, 0, 0);
 }
 
 static gboolean
@@ -868,8 +856,8 @@ gtk_socket_focus (GtkWidget *widget, GtkDirectionType direction)
          break;
        }
       
-      send_xembed_message (socket, XEMBED_FOCUS_IN, detail, 0, 0,
-                          gtk_get_current_event_time ());
+      _gtk_xembed_send_focus_message (socket->plug_window,
+                                     XEMBED_FOCUS_IN, detail);
 
       gtk_socket_claim_focus (socket, FALSE);
  
@@ -1017,7 +1005,7 @@ gtk_socket_add_window (GtkSocket        *socket,
       socket->xembed_version = -1;
       if (xembed_get_info (socket->plug_window, &version, &flags))
        {
-         socket->xembed_version = version;
+         socket->xembed_version = MIN (GTK_XEMBED_PROTOCOL_VERSION, version);
          socket->is_mapped = (flags & XEMBED_MAPPED) != 0;
        }
       else
@@ -1045,8 +1033,10 @@ gtk_socket_add_window (GtkSocket        *socket,
       if (toplevel && GTK_IS_WINDOW (toplevel))
        gtk_window_add_embedded_xid (GTK_WINDOW (toplevel), xid);
 
-      send_xembed_message (socket, XEMBED_EMBEDDED_NOTIFY, 0, 0, 0,
-                          gtk_get_current_event_time ());
+      _gtk_xembed_send_message (socket->plug_window,
+                               XEMBED_EMBEDDED_NOTIFY, 0,
+                               GDK_WINDOW_XWINDOW (widget->window),
+                               socket->xembed_version);
       socket_update_active (socket);
       socket_update_focus_in (socket);
 
@@ -1057,42 +1047,6 @@ gtk_socket_add_window (GtkSocket        *socket,
     g_signal_emit (socket, socket_signals[PLUG_ADDED], 0);
 }
 
-
-static void
-send_xembed_message (GtkSocket *socket,
-                    glong      message,
-                    glong      detail,
-                    glong      data1,
-                    glong      data2,
-                    guint32    time)
-{
-  GTK_NOTE(PLUGSOCKET,
-        g_message ("GtkSocket: Sending XEMBED message of type %ld", message));
-  
-  if (socket->plug_window)
-    {
-      GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (socket));
-      XEvent xevent;
-
-      xevent.xclient.window = GDK_WINDOW_XWINDOW (socket->plug_window);
-      xevent.xclient.type = ClientMessage;
-      xevent.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED");
-      xevent.xclient.format = 32;
-      xevent.xclient.data.l[0] = time;
-      xevent.xclient.data.l[1] = message;
-      xevent.xclient.data.l[2] = detail;
-      xevent.xclient.data.l[3] = data1;
-      xevent.xclient.data.l[4] = data2;
-
-      gdk_error_trap_push ();
-      XSendEvent (GDK_DISPLAY_XDISPLAY (display),
-                 GDK_WINDOW_XWINDOW (socket->plug_window),
-                 False, NoEventMask, &xevent);
-      gdk_display_sync (display);
-      gdk_error_trap_pop ();
-    }
-}
-
 static gboolean
 xembed_get_info (GdkWindow     *window,
                 unsigned long *version,
@@ -1146,15 +1100,80 @@ xembed_get_info (GdkWindow     *window,
 }
 
 static void
-handle_xembed_message (GtkSocket *socket,
-                      glong      message,
-                      glong      detail,
-                      glong      data1,
-                      glong      data2,
-                      guint32    time)
+advance_toplevel_focus (GtkSocket        *socket,
+                       GtkDirectionType  direction)
+{
+  GtkBin *bin;
+  GtkWindow *window;
+  GtkContainer *container;
+  GtkWidget *toplevel;
+  GtkWidget *old_focus_child;
+  GtkWidget *parent;
+
+  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
+  if (!toplevel)
+    return;
+
+  if (!GTK_WIDGET_TOPLEVEL (toplevel) || GTK_IS_PLUG (toplevel))
+    {
+      gtk_widget_child_focus (toplevel,direction);
+      return;
+    }
+
+  container = GTK_CONTAINER (toplevel);
+  window = GTK_WINDOW (toplevel);
+  bin = GTK_BIN (toplevel);
+
+  /* This is a copy of gtk_window_focus(), modified so that we
+   * can detect wrap-around.
+   */
+  old_focus_child = container->focus_child;
+  
+  if (old_focus_child)
+    {
+      if (gtk_widget_child_focus (old_focus_child, direction))
+       return;
+
+      /* We are allowed exactly one wrap-around per sequence of focus
+       * events
+       */
+      if (_gtk_xembed_get_focus_wrapped ())
+       return;
+      else
+       _gtk_xembed_set_focus_wrapped ();
+    }
+
+  if (window->focus_widget)
+    {
+      /* Wrapped off the end, clear the focus setting for the toplevel */
+      parent = window->focus_widget->parent;
+      while (parent)
+       {
+         gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
+         parent = GTK_WIDGET (parent)->parent;
+       }
+      
+      gtk_window_set_focus (GTK_WINDOW (container), NULL);
+    }
+
+  /* Now try to focus the first widget in the window */
+  if (bin->child)
+    {
+      if (gtk_widget_child_focus (bin->child, direction))
+        return;
+    }
+}
+
+static void
+handle_xembed_message (GtkSocket        *socket,
+                      XEmbedMessageType message,
+                      glong             detail,
+                      glong             data1,
+                      glong             data2,
+                      guint32           time)
 {
   GTK_NOTE (PLUGSOCKET,
-           g_message ("GtkSocket: Message of type %ld received", message));
+           g_message ("GtkSocket: Message of type %d received", message));
   
   switch (message)
     {
@@ -1165,7 +1184,7 @@ handle_xembed_message (GtkSocket *socket,
     case XEMBED_MODALITY_OFF:
     case XEMBED_FOCUS_IN:
     case XEMBED_FOCUS_OUT:
-      g_warning ("GtkSocket: Invalid _XEMBED message of type %ld received", message);
+      g_warning ("GtkSocket: Invalid _XEMBED message of type %d received", message);
       break;
       
     case XEMBED_REQUEST_FOCUS:
@@ -1174,16 +1193,10 @@ handle_xembed_message (GtkSocket *socket,
 
     case XEMBED_FOCUS_NEXT:
     case XEMBED_FOCUS_PREV:
-      {
-       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
-       if (toplevel)
-         {
-           gtk_widget_child_focus (toplevel,
-                                    (message == XEMBED_FOCUS_NEXT ?
-                                     GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD));
-         }
-       break;
-      }
+      advance_toplevel_focus (socket,
+                             (message == XEMBED_FOCUS_NEXT ?
+                              GTK_DIR_TAB_FORWARD : GTK_DIR_TAB_BACKWARD));
+      break;
       
     case XEMBED_GTK_GRAB_KEY:
       add_grabbed_key (socket, data1, data2);
@@ -1198,7 +1211,7 @@ handle_xembed_message (GtkSocket *socket,
       
     default:
       GTK_NOTE (PLUGSOCKET,
-               g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %ld", message));
+               g_message ("GtkSocket: Ignoring unknown _XEMBED message of type %d", message));
       break;
     }
 }
@@ -1250,13 +1263,14 @@ gtk_socket_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
     case ClientMessage:
       if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
        {
+         _gtk_xembed_push_message (xevent);
          handle_xembed_message (socket,
                                 xevent->xclient.data.l[1],
                                 xevent->xclient.data.l[2],
                                 xevent->xclient.data.l[3],
                                 xevent->xclient.data.l[4],
                                 xevent->xclient.data.l[0]);
-         
+         _gtk_xembed_pop_message ();
          
          return_val = GDK_FILTER_REMOVE;
        }
diff --git a/gtk/gtkxembed.c b/gtk/gtkxembed.c
new file mode 100644 (file)
index 0000000..bc1f5df
--- /dev/null
@@ -0,0 +1,210 @@
+/* GTK - The GIMP Toolkit
+ * gtkxembed.c: Utilities for the XEMBED protocol
+ * Copyright (C) 2001, 2003, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtkmain.h"
+#include "gtkprivate.h"
+#include "gtkxembed.h"
+
+typedef struct _GtkXEmbedMessage GtkXEmbedMessage;
+
+struct _GtkXEmbedMessage
+{
+  glong      message;
+  glong      detail;
+  glong      data1;
+  glong      data2;
+  guint32    time;
+};
+
+static GSList *current_messages;
+
+
+/**
+ * _gtk_xembed_push_message:
+ * @xevent: a XEvent
+ * 
+ * Adds a client message to the stack of current XEMBED events.
+ **/
+void
+_gtk_xembed_push_message (XEvent *xevent)
+{
+  GtkXEmbedMessage *message = g_new (GtkXEmbedMessage, 1);
+  
+  message->time = xevent->xclient.data.l[0];
+  message->message = xevent->xclient.data.l[1];
+  message->detail = xevent->xclient.data.l[2];
+  message->data1 = xevent->xclient.data.l[3];
+  message->data2 = xevent->xclient.data.l[4];
+
+  current_messages = g_slist_prepend (current_messages, message);
+}
+
+/**
+ * _gtk_xembed_pop_message:
+ * 
+ * Removes an event added with _gtk_xembed_push_message()
+ **/
+void
+_gtk_xembed_pop_message (void)
+{
+  GtkXEmbedMessage *message = current_messages->data;
+  current_messages = g_slist_delete_link (current_messages, current_messages);
+
+  g_free (message);
+}
+
+/**
+ * _gtk_xembed_set_focus_wrapped:
+ * 
+ * Sets a flag indicating that the current focus sequence wrapped
+ * around to the beginning of the ultimate toplevel.
+ **/
+void
+_gtk_xembed_set_focus_wrapped (void)
+{
+  GtkXEmbedMessage *message;
+  
+  g_return_if_fail (current_messages != NULL);
+  message = current_messages->data;
+  g_return_if_fail (message->message == XEMBED_FOCUS_PREV || message->message == XEMBED_FOCUS_NEXT);
+  
+  message->data1 |= XEMBED_FOCUS_WRAPAROUND;
+}
+
+/**
+ * _gtk_xembed_get_focus_wrapped:
+ * 
+ * Gets whether the current focus sequence has wrapped around
+ * to the beginning of the ultimate toplevel.
+ * 
+ * Return value: %TRUE if the focus sequence has wrapped around.
+ **/
+gboolean
+_gtk_xembed_get_focus_wrapped (void)
+{
+  GtkXEmbedMessage *message;
+  
+  g_return_val_if_fail (current_messages != NULL, FALSE);
+  message = current_messages->data;
+
+  return (message->data1 & XEMBED_FOCUS_WRAPAROUND) != 0;
+}
+
+static guint32
+gtk_xembed_get_time (void)
+{
+  if (current_messages)
+    {
+      GtkXEmbedMessage *message = current_messages->data;
+      return message->time;
+    }
+  else
+    return gtk_get_current_event_time ();
+}
+
+/**
+ * _gtk_xembed_send_message:
+ * @recipient: window to which to send the window, or %NULL
+ *             in which case nothing wil be sent
+ * @message:   type of message
+ * @detail:    detail field of message
+ * @data1:     data1 field of message
+ * @data2:     data2 field of message
+ * 
+ * Sends a generic XEMBED message to a particular window.
+ **/
+void
+_gtk_xembed_send_message (GdkWindow        *recipient,
+                         XEmbedMessageType message,
+                         glong             detail,
+                         glong             data1,
+                         glong             data2)
+{
+  GdkDisplay *display;
+  XEvent xevent;
+
+  if (!recipient)
+    return;
+         
+  g_return_if_fail (GDK_IS_WINDOW (recipient));
+
+  display = gdk_drawable_get_display (recipient);
+  GTK_NOTE (PLUGSOCKET,
+           g_message ("Sending XEMBED message of type %d", message));
+
+  xevent.xclient.window = GDK_WINDOW_XWINDOW (recipient);
+  xevent.xclient.type = ClientMessage;
+  xevent.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED");
+  xevent.xclient.format = 32;
+  xevent.xclient.data.l[0] = gtk_xembed_get_time ();
+  xevent.xclient.data.l[1] = message;
+  xevent.xclient.data.l[2] = detail;
+  xevent.xclient.data.l[3] = data1;
+  xevent.xclient.data.l[4] = data2;
+
+  gdk_error_trap_push ();
+  XSendEvent (GDK_WINDOW_XDISPLAY(recipient),
+             GDK_WINDOW_XWINDOW (recipient),
+             False, NoEventMask, &xevent);
+  gdk_display_sync (display);
+  gdk_error_trap_pop ();
+}
+
+/**
+ * _gtk_xembed_send_focus_message:
+ * @recipient: window to which to send the window, or %NULL
+ *             in which case nothing wil be sent
+ * @message:   type of message
+ * @detail:    detail field of message
+ * 
+ * Sends a XEMBED message for moving the focus along the focus
+ * chain to a window. The flags field that these messages share
+ * will be correctly filled in.
+ **/
+void
+_gtk_xembed_send_focus_message (GdkWindow        *recipient,
+                               XEmbedMessageType message,
+                               glong             detail)
+{
+  gulong flags = 0;
+  
+  g_return_if_fail (GDK_IS_WINDOW (recipient));
+  g_return_if_fail (message == XEMBED_FOCUS_IN ||
+                   message == XEMBED_FOCUS_NEXT ||
+                   message == XEMBED_FOCUS_PREV);
+                   
+  if (current_messages)
+    {
+      GtkXEmbedMessage *message = current_messages->data;
+      switch (message->message)
+       {
+       case XEMBED_FOCUS_IN:
+       case XEMBED_FOCUS_NEXT:
+       case XEMBED_FOCUS_PREV:
+         flags = message->data1 & XEMBED_FOCUS_WRAPAROUND;
+         break;
+       default:
+         break;
+       }
+    }
+
+  _gtk_xembed_send_message (recipient, message, detail, flags, 0);
+}
+
diff --git a/gtk/gtkxembed.h b/gtk/gtkxembed.h
new file mode 100644 (file)
index 0000000..d18e81a
--- /dev/null
@@ -0,0 +1,48 @@
+/* GTK - The GIMP Toolkit
+ * gtkxembed.c: Utilities for the XEMBED protocol
+ * Copyright (C) 2003, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_XEMBED_H__
+#define __GTK_XEMBED_H__
+
+#include "xembed.h"
+#include "x11/gdkx.h"
+
+G_BEGIN_DECLS
+
+/* Latest version we implement */
+#define GTK_XEMBED_PROTOCOL_VERSION 1
+
+void _gtk_xembed_send_message       (GdkWindow         *recipient,
+                                    XEmbedMessageType  message,
+                                    glong              detail,
+                                    glong              data1,
+                                    glong              data2);
+void _gtk_xembed_send_focus_message (GdkWindow         *recipient,
+                                    XEmbedMessageType  message,
+                                    glong              detail);
+
+void     _gtk_xembed_push_message       (XEvent    *xevent);
+void     _gtk_xembed_pop_message        (void);
+void     _gtk_xembed_set_focus_wrapped  (void);
+gboolean _gtk_xembed_get_focus_wrapped  (void);
+
+G_END_DECLS
+
+#endif /*  __GTK_XEMBED_H__ */
index 14d55e604d02e7e689f5c3144d61386efcc909a8..106392951ee252d6e4decfe1d6dda0738a8478af 100644 (file)
@@ -1,26 +1,31 @@
 /* XEMBED messages */
-#define XEMBED_EMBEDDED_NOTIFY          0
-#define XEMBED_WINDOW_ACTIVATE          1
-#define XEMBED_WINDOW_DEACTIVATE        2
-#define XEMBED_REQUEST_FOCUS            3
-#define XEMBED_FOCUS_IN                 4
-#define XEMBED_FOCUS_OUT                5
-#define XEMBED_FOCUS_NEXT               6
-#define XEMBED_FOCUS_PREV               7
-#define XEMBED_GRAB_KEY                 8
-#define XEMBED_UNGRAB_KEY               9
-#define XEMBED_MODALITY_ON              10
-#define XEMBED_MODALITY_OFF             11
+typedef enum {
+  XEMBED_EMBEDDED_NOTIFY        = 0,
+  XEMBED_WINDOW_ACTIVATE        = 1,
+  XEMBED_WINDOW_DEACTIVATE      = 2,
+  XEMBED_REQUEST_FOCUS          = 3,
+  XEMBED_FOCUS_IN               = 4,
+  XEMBED_FOCUS_OUT              = 5,
+  XEMBED_FOCUS_NEXT             = 6,
+  XEMBED_FOCUS_PREV             = 7,
+  XEMBED_GRAB_KEY               = 8,
+  XEMBED_UNGRAB_KEY             = 9,
+  XEMBED_MODALITY_ON            = 10,
+  XEMBED_MODALITY_OFF           = 11,
 
 /* Non standard messages*/
-#define XEMBED_GTK_GRAB_KEY             108 
-#define XEMBED_GTK_UNGRAB_KEY           109
+  XEMBED_GTK_GRAB_KEY           = 108, 
+  XEMBED_GTK_UNGRAB_KEY         = 109
+} XEmbedMessageType;
 
 /* Details for  XEMBED_FOCUS_IN: */
-#define XEMBED_FOCUS_CURRENT            0
-#define XEMBED_FOCUS_FIRST              1
-#define XEMBED_FOCUS_LAST               2
+#define XEMBED_FOCUS_CURRENT             0
+#define XEMBED_FOCUS_FIRST               1
+#define XEMBED_FOCUS_LAST                2
 
+/* Flags for XEMBED_FOCUS_IN, XEMBED_FOCUS_NEXT, XEMBED_FOCUS_PREV */
+#define XEMBED_FOCUS_WRAPAROUND         (1 << 0)
 
 /* Flags for _XEMBED_INFO */
 #define XEMBED_MAPPED                   (1 << 0)
+